package com.iwthings.x264;

import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.List;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import android.annotation.SuppressLint;
import android.graphics.ImageFormat;
import android.hardware.Camera;
import android.hardware.Camera.ErrorCallback;
import android.hardware.Camera.PreviewCallback;
import android.hardware.Camera.Size;
import android.os.Build;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.SurfaceHolder.Callback;

public class X264Recoder implements PreviewCallback, ErrorCallback,
		SurfaceHolder.Callback {
	Lock lock = null;
	Camera m_camera = null;
	SurfaceHolder m_holderPrevewview = null;

	EncoderThread m_thEncode = null;
	boolean m_bCall = false;
	byte[] m_camerabuf = null;
	byte[] m_yuvbuf = null;
	byte[] m_tmpbuf = null;
	int m_width = 320;
	int m_height = 240;
	int m_picturesize = 0;
	long timeFrame;
	int timelost;

	public int OpenRecoder(int userid, String ip, int port, int width,
			int height, int framerate, int bitrate, SurfaceView surfaceview) {
		String strip;
		lock = new ReentrantLock();
		m_width = width;
		m_height = height;

		m_holderPrevewview = surfaceview.getHolder(); // 绑定SurfaceView，取得SurfaceHolder对象
		m_holderPrevewview.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
		m_holderPrevewview.addCallback((Callback) this);

		try {
			InetAddress myServer = InetAddress.getByName(ip);
			strip = myServer.getHostAddress();
		} catch (UnknownHostException e) {
			strip = "124.202.151.53";
		}
		int iret = X264Jni.GetInstance().OpenX264Encoder(userid, strip, port,
				width, height, bitrate, framerate, 20);
		if (iret < 0) {
			return -1;
		}

		timelost = 1000 / framerate;
		timeFrame = System.currentTimeMillis();
		m_picturesize = width * height * 3 / 2;
		m_camerabuf = new byte[m_picturesize];
		m_yuvbuf = new byte[m_picturesize];
		m_tmpbuf = new byte[m_picturesize];

		if (openCamera() < 0) {
			return -1;
		}

		m_thEncode = new EncoderThread();
		m_thEncode.start();

		return 0;

	}

	public void CloseRecoder() {
		if (m_thEncode != null) {
			m_thEncode.stopEncoderThread();
			m_thEncode = null;
		}

		if (m_camera != null) {
			closeCamera();
			m_camera = null;
		}
		X264Jni.GetInstance().CloseX264Encoder();
	}

	@SuppressLint("NewApi")
	int openCamera() {
		try {
			if (Build.VERSION.SDK_INT > 8) {
				int num = Camera.getNumberOfCameras();
				if (num < 1) {
					return -1;
				}
				m_camera = Camera.open(num - 1);
			} else {
				m_camera = Camera.open();
			}
			m_camera.setPreviewDisplay(m_holderPrevewview);
			Camera.Parameters parameters = m_camera.getParameters();
			List<Size> supportedPictureSizes = parameters
					.getSupportedPictureSizes();
			List<Size> supportedPreviewSizes = parameters
					.getSupportedPreviewSizes();
			parameters.setPreviewSize(m_width, m_height);
			parameters.setPictureSize(m_width, m_height);
			parameters.setPreviewFormat(ImageFormat.NV21);
			m_camera.setParameters(parameters);
			m_camera.addCallbackBuffer(m_camerabuf);
			m_camera.setPreviewCallbackWithBuffer(this);
			m_camera.setErrorCallback(this);
			m_camera.startPreview();
		} catch (IOException e) {
			e.printStackTrace();
			return -1;
		}

		return 0;
	}

	void closeCamera() {
		if (m_camera != null) {
			m_camera.setPreviewCallback(null); // ！！这个必须在前，不然退出出错
			m_camera.stopPreview();
			m_camera.release();
			m_camera = null;
		}
	}

	@Override
	public void onError(int error, Camera camera) {
		// TODO Auto-generated method stub
		closeCamera();
		openCamera();
	}

	@Override
	public void onPreviewFrame(byte[] data, Camera camera) {
		lock.lock();
		System.arraycopy(data, 0, m_yuvbuf, 0, m_picturesize);
		m_bCall = true;
		lock.unlock();

		m_camera.addCallbackBuffer(data);
	}

	class EncoderThread extends Thread {
		public void stopEncoderThread() {
			while (true) {
				if (this.isAlive()) {
					this.interrupt();
				} else {
					break;
				}
			}
		}

		public void run() {
			while (!this.isInterrupted()) {

				lock.lock();
				if (m_bCall == false) {
					lock.unlock();
					try {
						Thread.sleep(5);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					continue;
				}

				if (System.currentTimeMillis() > timeFrame) {
					System.arraycopy(m_yuvbuf, 0, m_tmpbuf, 0, m_picturesize);
					m_bCall = false;

					lock.unlock();
					timeFrame += timelost;
					X264Jni.GetInstance().X264Encoder(m_tmpbuf);
				} else {
					lock.unlock();
				}

			}
		}
	}

	@Override
	public void surfaceChanged(SurfaceHolder holder, int format, int width,
			int height) {
		// TODO Auto-generated method stub

	}

	@Override
	public void surfaceCreated(SurfaceHolder holder) {
		// TODO Auto-generated method stub

	}

	@Override
	public void surfaceDestroyed(SurfaceHolder holder) {
		// TODO Auto-generated method stub
		CloseRecoder();
	}

}
